
namespace apeng {

    export interface IRectangle {
        x: number
        y: number
        width: number
        height: number
    }
    const v2T = new Vector2()
    const v2T2 = new Vector2()
    const sizeT = {
        width: 0,
        height: 0
    }


    type TRectangleSet = IRectangle | number

    export class Rectangle implements IRectangle {
        public static TEMP = new Rectangle()

        public static set(self: IRectangle, x?: TRectangleSet, y?: number, width?: number, height?: number): IRectangle {
            if (x && typeof x === "object") {
                self.x = x.x
                self.y = x.y
                self.width = x.width || 0
                self.height = x.height || 0
            } else {
                self.x = (x || 0) as number
                self.y = y || 0
                self.width = width || 0
                self.height = height || 0
            }
            return self
        }

        /**由任意两个点创建一个矩形，目标矩形即是这两个点各向 x、y 轴作线所得到的矩形 */
        public static fromMinMax(out: IRectangle, min: IVector2, max: IVector2): IRectangle {
            const minX = Math.min(min.x, max.x)
            const minY = Math.min(min.y, max.y)
            const maxX = Math.max(min.x, max.x)
            const maxY = Math.max(min.y, max.y)
            out.x = minX
            out.y = minY
            out.width = maxX - minX
            out.height = maxY - minY
            return out
        }

        /**xy是否在近似值内 */
        public static equalsXY(self: IRectangle, other: IRectangle, range: number): boolean {
            if (self.width != other.width)
                return false
            if (self.height != other.height)
                return false

            if (!Maths.isRange(self.x, other.x, range))
                return false

            if (!Maths.isRange(self.y, other.y, range))
                return false
            return true
        }

        /**计算当前矩形与指定矩形重叠部分的矩形，将其赋值给出口矩形 */
        public static intersection(out: IRectangle, self: IRectangle, other: IRectangle): IRectangle {
            const axMin = self.x
            const ayMin = self.y
            const axMax = self.x + self.width
            const ayMax = self.y + self.height
            const bxMin = other.x
            const byMin = other.y
            const bxMax = other.x + other.width
            const byMax = other.y + other.height
            out.x = Math.max(axMin, bxMin)
            out.y = Math.max(ayMin, byMin)
            out.width = Math.min(axMax, bxMax) - out.x
            out.height = Math.min(ayMax, byMax) - out.y
            return out
        }

        /**创建同时包含当前矩形和指定矩形的最小矩形，将其赋值给出口矩形 */
        public static union<T extends IRectangle>(out: T, self: IRectangle, other: IRectangle): T {
            const x = self.x
            const y = self.y
            const w = self.width
            const h = self.height
            const bx = other.x
            const by = other.y
            const bw = other.width
            const bh = other.height
            out.x = Math.min(x, bx)
            out.y = Math.min(y, by)
            out.width = Math.max(x + w, bx + bw) - out.x
            out.height = Math.max(y + h, by + bh) - out.y
            return out
        }

        /**判断当前矩形是否包含指定矩形 */
        public static containsRect(self: IRectangle, other: IRectangle): boolean {
            return (self.x <= other.x
                && self.x + self.width >= other.x + other.width
                && self.y <= other.y
                && self.y + self.height >= other.y + other.height)
        }

        /**判断当前矩形是否包含指定的点 */
        public static contains(self: Rectangle, point: IVector2) {
            return (self.x < point.x
                && self.x + self.width > point.x
                && self.y < point.y
                && self.y + self.height > point.y)
        }

        /**判断当前矩形是否与指定矩形相交 */
        public static intersects(self: IRectangle, other: IRectangle): boolean {
            const maxax = self.x + self.width
            const maxay = self.y + self.height
            const maxbx = other.x + other.width
            const maxby = other.y + other.height
            return !(maxax < other.x || maxbx < self.x || maxay < other.y || maxby < self.y)
        }

        /**判断当前矩形是否与指定矩形相等 */
        public static equals(self: IRectangle, other: IRectangle): boolean {
            return self.x === other.x
                && self.y === other.y
                && self.width === other.width
                && self.height === other.height;
        }

        /**绕中心旋转矩形 */
        public static angleCenter(out: Vector2[], value: Rectangle, angle: number): Vector2[] {
            let center = value.center
            let size = value.size
            let radian = Maths.toRadian(angle)
            Vector2.set(out[0], -size.width / 2, -size.height / 2)
            Vector2.set(out[1], size.width / 2, -size.height / 2)
            Vector2.set(out[2], size.width / 2, size.height / 2)
            Vector2.set(out[3], -size.width / 2, size.height / 2)
            for (let _out of out)
                _out.rotateSelf(radian).addSelf(center)
            return out
        }

        /**根据指定的插值比率，从当前矩形到目标矩形之间做插值 */
        public static lerp(out: IRectangle, self: IRectangle, to: IRectangle, ratio: number): IRectangle {
            const x = self.x
            const y = self.y
            const w = self.width
            const h = self.height
            out.x = x + (to.x - x) * ratio
            out.y = y + (to.y - y) * ratio
            out.width = w + (to.width - w) * ratio
            out.height = h + (to.height - h) * ratio

            return out
        }

        /**距中心点 缩放矩形 */
        public static mul(out: Rectangle, value: Rectangle, mul: TVectorSet): Rectangle {
            v2T2.set(value.center)
            v2T.set(mul, mul as number)
            out.width = value.width * v2T.x
            out.height = value.height * v2T.y
            out.center = v2T2
            return out
        }

        /**从中心点 增加范围 */
        public static add(out: Rectangle, value: Rectangle, addWidth: number, addHeight: number): Rectangle {
            v2T2.set(value.center)
            out.width = value.width + addWidth
            out.height = value.height + addHeight
            out.center = v2T2
            return out
        }


        /**
         * 等分矩形到位置
         * @param x 
         * @param y 
         * @param center 排除边框点 全部居中排列 
         */
        public static layout(out: IVector2[][], rectange: IRectangle, xCount: number, yCount: number, center: boolean): IVector2[][] {
            for (let i = 0; i < xCount; i++) {
                if (out[i] == undefined)
                    out[i] = []
                for (let j = 0; j < yCount; j++) {
                    if (out[i][j] == undefined)
                        out[i][j] = new Vector2()
                    Rectangle.layoutPoint(out[i][j], rectange, i, j, xCount, yCount, center)
                }
            }
            return out
        }


        /**
         * 将 self里的一个坐标 
         * 转换到 other里的坐标
         * @param out 
         * @param self 
         * @param other 
         */
        public static converPointToOther(out: IVector2, point: IVector2, self: Rectangle, other: Rectangle): IVector2 {
            Vector2.sub(out, self.origin, other.origin)
            Vector2.add(out, out, point)
            return out
        }

        public static layoutPoint(out: IVector2, rectange: IRectangle, i: number, j: number, xCount: number, yCount: number, center: boolean) {
            if (center) {
                out.x = rectange.x + ((i + .5) / xCount) * rectange.width
                out.y = rectange.y + ((j + .5) / yCount) * rectange.height
            }
            else {
                out.x = rectange.x + (i / (xCount - 1)) * rectange.width
                out.y = rectange.y + (j / (yCount - 1)) * rectange.height
            }
            return out
        }

        public static _layoutPointByIndex: { i: number, j: number } = <any>{}
        public static layoutPointByIndex(rectange: Rectangle, point: IVector2, xCount: number, yCount: number, center: boolean): { i: number, j: number } {
            if (center) {
                Vector2.sub(v2T, point, rectange.origin)
                Rectangle._layoutPointByIndex.i = Math.floor(v2T.x / (xCount - .5))
                Rectangle._layoutPointByIndex.j = Math.floor(v2T.y / (yCount - .5))
            }
            else {

            }
            return Rectangle._layoutPointByIndex
        }

        public static create(x?: number, y?: number, width?: number, height?: number): Rectangle {
            return new Rectangle(x, y, width, height)
        }

        public static clone(rectange: Rectangle): Rectangle {
            return new Rectangle().set(rectange)
        }

        public static polygon(out: Vector2[], value: Rectangle): Vector2[] {
            Vector2.set(out[0], value.xMin, value.yMin)
            Vector2.set(out[1], value.xMin, value.yMax)
            Vector2.set(out[2], value.xMax, value.yMax)
            Vector2.set(out[3], value.xMax, value.yMin)
            return out
        }

        /**将当前包围盒分成4等分的矩形 */
        public static quadtree(out: Rectangle[], value: Rectangle): Rectangle[] {
            let width = value.width / 2
            let height = value.height / 2
            for (let i = 0; i < 4; i++) {
                out[i].width = width
                out[i].height = height
            }

            // 左上角
            out[0].origin = v2T.set(value.x, value.y + height)
            // 右上角
            out[1].origin = v2T.set(value.x + width, value.y + height)
            // 左下角
            out[2].origin = v2T.set(value.x, value.y)
            // 右下角
            out[3].origin = v2T.set(value.x + width, value.y)
            return out
        }


        public x: number = 0
        public y: number = 0
        public width: number = 0
        public height: number = 0
        public angle: number = 0

        /**获取或设置矩形的 x 和 y 坐标 */
        private _origin: Vector2 = new Vector2()
        public get origin(): IVector2 {
            return this._origin.set(this.x, this.y)
        }

        public set origin(value: IVector2) {
            this.x = value.x
            this.y = value.y
        }

        /**获取或设置矩形中心点的坐标 */
        private _center: Vector2 = new Vector2()
        public get center(): IVector2 {
            return this._center.set(this.x + this.width / 2, this.y + this.height / 2)
        }

        public set center(value: IVector2) {
            this.x = value.x - this.width / 2
            this.y = value.y - this.height / 2
        }
        /**中心点相对于左边的距离 */
        public get centerDisLeft(): number {
            return this.center.x - this.width / 2
        }
        public get centerDisRight() {
            return this.center.x + this.width / 2
        }
        public get centerDisTop() {
            return this.center.y + this.height / 2
        }
        public get centerDisDown() {
            return this.center.y - this.height / 2
        }

        public get yMax() {
            return this.y + this.height
        }
        public set yMax(value) {
            this.height = value - this.y
        }

        public get yMin() {
            return this.y
        }
        public set yMin(value) {
            this.height += this.y - value
            this.y = value
        }

        public get xMax() {
            return this.x + this.width
        }
        public set xMax(value) {
            this.width = value - this.x
        }


        public get xMin() {
            return this.x
        }

        public set xMin(value) {
            this.width += this.x - value
            this.x = value
        }
        /**获取或设置矩形的尺寸 */
        get size() {
            sizeT.width = this.width
            sizeT.height = this.height
            return sizeT
        }

        set size(value) {
            this.width = value.width
            this.height = value.height
        }

        private _v2s: Vector2[] = null!
        public get v2s() {
            if (this._v2s == null)
                this._v2s = [new Vector2(), new Vector2(), new Vector2(), new Vector2()]
            return this._v2s
        }

        constructor(x?: number, y?: number, width?: number, height?: number) {
            this.set(x, y, width, height)
        }

        private _layoutOut: IVector2[][] = null!
        public _layoutXCount: number = null!
        public _layoutYCount: number = null!
        public _layoutCenter: boolean = null!
        public layoutSelf(xCount: number, yCount: number, center: boolean) {
            if (this._layoutOut == null)
                this._layoutOut = []
            this._layoutXCount = xCount
            this._layoutYCount = yCount
            this._layoutCenter = center
            return Rectangle.layout(this._layoutOut, this, this._layoutXCount, this._layoutYCount, this._layoutCenter)
        }

        public layoutPoint(out: IVector2, i: number, j: number): IVector2 { return Rectangle.layoutPoint(out, this, i, j, this._layoutXCount, this._layoutYCount, this._layoutCenter) }
        public layoutPointByIndex(point: IVector2) { return Rectangle.layoutPointByIndex(this, point, this._layoutXCount, this._layoutYCount, this._layoutCenter) }

        public polygon(): Vector2[] { return Rectangle.polygon(this.v2s, this) }
        public set(x?: TRectangleSet, y?: number, width?: number, height?: number) { return Rectangle.set(this, x, y, width, height) as Rectangle }
        public lerp(out: IRectangle, to: IRectangle, ratio: number) { return Rectangle.lerp(out, this, to, ratio) }
        public lerpSelf(to: IRectangle, ratio: number) { return Rectangle.lerp(this, this, to, ratio) as Rectangle }
        public equals(other: IRectangle) { return Rectangle.equals(this, other) }
        public intersects(other: IRectangle) { return Rectangle.intersects(this, other) }
        public contains(point: IVector2) { return Rectangle.contains(this, point) }
        public containsRect(other: IRectangle) { return Rectangle.containsRect(this, other) }
        public union(out: IRectangle, other: IRectangle) { return Rectangle.union(out, this, other) }
        public unionSelf(other: IRectangle) { return Rectangle.union(this, this, other) }
        public intersection(out: IRectangle, other: IRectangle) { return Rectangle.intersection(out, this, other) }
        public intersectionSelf(other: IRectangle) { return Rectangle.intersection(this, this, other) }
        public equalsXY(other: IRectangle, range: number) { return Rectangle.equalsXY(this, other, range) }
        public mul(out: Rectangle, mul: TVectorSet) { return Rectangle.mul(out, this, mul) }
        public mulSelf(mul: TVectorSet) { return Rectangle.mul(this, this, mul) }
        public add(out: Rectangle, addWidth: number, addHeight: number) { return Rectangle.add(out, this, addWidth, addHeight) }
        public addSelf(addWidth: number, addHeight: number) { return Rectangle.add(this, this, addWidth, addHeight) }
        public layout(out: IVector2[][], xCount: number, yCount: number, center: boolean) { return Rectangle.layout(out, this, xCount, yCount, center) }
        public converPointToOther(out: IVector2, point: IVector2, other: Rectangle) { return Rectangle.converPointToOther(out, point, this, other) }
        public fromMinMax(min: IVector2, max: IVector2) { return Rectangle.fromMinMax(this, min, max) }
        public clone() { return Rectangle.clone(this) }
        public angleCenter(addAngle = 0) { return this.angle != 0 ? Rectangle.angleCenter(this.v2s, this, this.angle + addAngle) : Rectangle.polygon(this.v2s, this) }

        private _quadtree: Rectangle[] = null!
        public quadtree() {
            if (this._quadtree == null) {
                this._quadtree = []
                for (let i = 0; i < 4; i++)
                    this._quadtree[i] = new Rectangle()
            }
            return Rectangle.quadtree(this._quadtree, this)
        }


    }

    const rectT = new Rectangle()

}

